package cs.rsa.ts14.lima;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

import cs.rsa.ts14.framework.LineType;
import cs.rsa.ts14.framework.LineTypeClassifierStrategy;

/**
 * 
 * A hopefully more reliable implementation of the line type classifier, that
 * can pass our full test case suite.
 * 
 * We now we could probably use regular expressions to match lines to line type,
 * but that is too complicated. :-)
 * 
 * @author Group Lima - RSA 2014
 * 
 */
public class GroupLimaLineTypeClassifierStrategy implements
		LineTypeClassifierStrategy {

	LineType lastSeen;
	String lastLineSeen = "";

	List<String> weekdays = Arrays.asList("Mon", "Tue", "Wed", "Thu", "Fri",
			"Sat", "Sun");
	List<String> transportionModes = Arrays.asList("Bi", "Ca", "Pu", "Tr",
			"No", "Ho");

	@Override
	public LineType classify(String line) {

		// Default line type if we cannot match to any other line type
		lastSeen = LineType.INVALID_LINE;
		lastLineSeen = line;

		// If line is empty or contains only spaces, it is a empty line
		if (line.length() < 1 || line.trim().length() < 1) {
			lastSeen = LineType.EMPTY_LINE;
			return lastSeen;
		}

		// If line starts with # it is a comment
		if (line.charAt(0) == '#') {
			lastSeen = LineType.COMMENT_LINE;
			return lastSeen;
		}

		// For later processing we need tokens and token count
		String tokenArray[] = Util.buildTokenArray(line);
		int numberOfTokens = tokenArray.length;

		// If line has 2 or more tokens it might be a weekday specification
		if (numberOfTokens >= 2) {
			if (weekdays.contains(tokenArray[0])
					&& transportionModes.contains(tokenArray[1])) {
				lastSeen = LineType.WEEKDAY_SPECIFICATION;
				return lastSeen;
			}
		}

		// If line has 3 tokens it is work specification or assignment line
		if (numberOfTokens == 3) {

			// This seems like a work specification
			if (Character.isWhitespace(line.charAt(0))
					&& Util.isAlpha(tokenArray[0].substring(0, 1))
					&& (Util.isAlpha(tokenArray[1].substring(0, 1)) || tokenArray[1]
							.equals("-"))) {
				try {
					// This seems like a work specification line, but check if
					// 3d token is a number and have a remainder of 0 when
					// divided by 0.5
					Double hours = Double.parseDouble(tokenArray[2]);
					if (BigDecimal.valueOf(hours)
							.remainder(BigDecimal.valueOf(0.5)).doubleValue() == 0) {
						lastSeen = LineType.WORK_SPECIFICATION;
						return lastSeen;
					}
				} catch (NumberFormatException e) {
					// token was not a number
				}

			} else if (Util.isAlpha(tokenArray[0]) && tokenArray[1].equals("=")) {
				try {
					// This seems like a assignment line, but check if 3d token
					// is a number and have a remainder of 0 when divided by 0.5
					Double.parseDouble(tokenArray[2]);
					lastSeen = LineType.ASSIGNMENT_LINE;
					return lastSeen;

				} catch (NumberFormatException e) {
					// token was not a number
				}
			}
		} else if (numberOfTokens == 6) {
			if (tokenArray[0].equals("Week") && tokenArray[2].equals(":")
					&& tokenArray[4].equals(":")) {
				try {
					int weekNumber = Integer.parseInt(tokenArray[1]);
					int workdays = Integer.parseInt(tokenArray[3]);
					int holidays = Integer.parseInt(tokenArray[5]);

					if (weekNumber >= 1 && weekNumber <= 53 && workdays >= 0
							&& workdays <= 5 && holidays >= 0 && holidays <= 5
							&& (holidays + workdays) <= 5) {
						lastSeen = LineType.WEEK_SPECIFICATION;
						return lastSeen;
					}
				} catch (NumberFormatException e) {
					// token was not an integer
				}
			}
		}

		// Return invalid line as default
		return lastSeen;
	}

	@Override
	public String lastError() {
		if (lastSeen == LineType.INVALID_LINE)
			return "Invalid line: " + lastLineSeen;
		return "No error";
	}

}
